12-2 多数据库初始化:TypeORM CLI配置(结合ormconfig)
1. 多数据库环境搭建
1.1 创建新数据库实例
通过Docker Compose创建第二个MySQL数据库实例:
services:
db1:
image: mysql:8.0
ports:
- "3307:3306" # 映射到主机3307端口
environment:
MYSQL_ROOT_PASSWORD: example
volumes:
- db1_data:/var/lib/mysql # 持久化数据卷
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 初始化脚本
volumes:
db1_data:
yaml
1.1.1 关键配置解析
- 端口映射:
3307:3306
表示将容器内3306端口映射到主机3307端口 - 数据持久化:通过
volumes
挂载数据卷,避免容器重启数据丢失 - 初始化脚本:
/docker-entrypoint-initdb.d/
目录下的SQL文件会在容器首次启动时自动执行
💡生产环境中建议:
- 使用自定义MySQL配置文件
- 设置合理的资源限制(CPU/内存)
- 启用日志轮转功能
1.1.2 启动与验证
# 启动服务
docker-compose -f docker-compose.yml up -d
# 查看运行状态
docker-compose ps
# 查看日志
docker-compose logs -f db1
bash
1.2 验证数据库连接
1.2.1 连接方式对比
连接方式 | 地址示例 | 适用场景 |
---|---|---|
Docker服务名 | db1:3306 | 同一Docker网络内的容器间通信 |
主机端口 | localhost:3307 | 宿主机直接访问 |
远程IP | 192.168.1.100:3307 | 跨主机访问 |
1.2.2 常用客户端工具
- 命令行工具:
mysql -h db1 -u root -p
bash
- 可视化工具:
- MySQL Workbench
- DBeaver
- Navicat
💡连接问题排查:
- 检查防火墙设置
- 验证Docker网络配置
- 查看MySQL用户权限
1.3 初始化测试数据库
1.3.1 自动化初始化
创建init.sql
文件:
-- 创建测试数据库
CREATE DATABASE IF NOT EXISTS test_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建测试用户
CREATE USER 'test_user'@'%' IDENTIFIED BY 'test_password';
GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'%';
FLUSH PRIVILEGES;
sql
1.3.2 初始化验证
-- 查看数据库列表
SHOW DATABASES;
-- 查看用户权限
SELECT host, user FROM mysql.user;
sql
1.4 多数据库架构设计
1.4.1 典型应用场景
- 读写分离:主库写,从库读
- 分库分表:按业务垂直拆分
- 多租户:每个租户独立数据库
1.4.2 连接池配置建议
// TypeORM连接池配置示例
{
extra: {
connectionLimit: 10, // 最大连接数
acquireTimeout: 30000, // 获取连接超时(ms)
waitForConnections: true // 无可用连接时等待
}
}
typescript
💡扩展阅读:
2. TypeORM CLI配置
2.1 创建ormconfig文件
2.1.1 配置文件深度解析
// 核心配置项说明
{
type: 'mysql', // 支持mysql/postgres/sqlite/mssql等
host: 'localhost', // 支持Docker服务名/云数据库地址
port: 3306, // 注意类型为number
username: 'root', // 生产环境应使用专用账号
password: 'secret', // 建议通过vault管理
database: 'test_db', // 数据库名称
synchronize: false, // 生产环境必须关闭!
logging: ['query', 'error'], // 开发时可开启查询日志
entities: [
'dist/**/*.entity.js' // 生产环境路径
],
migrations: [
'dist/migrations/*.js' // 迁移文件路径
],
subscribers: [
'dist/subscribers/*.js' // 订阅者路径
],
cli: {
migrationsDir: 'src/migrations' // 迁移文件生成目录
}
}
typescript
2.1.2 多环境配置方案
- 环境隔离策略:
// config/ormconfig.ts
const env = process.env.NODE_ENV || 'development';
const baseConfig: DataSourceOptions = {
// 公共配置
};
const envConfigs = {
development: { synchronize: true },
test: { database: 'test_db' },
production: {
url: process.env.DATABASE_URL,
extra: { ssl: { rejectUnauthorized: false } }
}
};
export default new DataSource({
...baseConfig,
...envConfigs[env]
});
typescript
- 动态加载实现:
function loadConfig(): DataSourceOptions {
const envPath = `.env.${process.env.NODE_ENV}`;
if (fs.existsSync(envPath)) {
dotenv.config({ path: envPath });
}
return {
// 动态配置逻辑
};
}
typescript
2.2 高级配置技巧
2.2.1 连接池优化
extra: {
connectionLimit: 10,
idleTimeout: 30000,
maxIdle: 5,
queueLimit: 0
}
typescript
2.2.2 SSL连接配置
ssl: process.env.NODE_ENV === 'production' ? {
ca: fs.readFileSync(__dirname + '/ssl/ca-cert.pem'),
rejectUnauthorized: true
} : false
typescript
2.3 npm脚本增强
2.3.1 完整脚本集
"scripts": {
"typeorm": "ts-node ./node_modules/typeorm/cli",
"db:migrate": "npm run typeorm migration:run",
"db:revert": "npm run typeorm migration:revert",
"db:create-migration": "npm run typeorm migration:generate -- -n",
"db:sync": "npm run typeorm schema:sync",
"db:drop": "npm run typeorm schema:drop"
}
json
2.3.2 安全增强方案
- 敏感信息保护:
# 安装加密工具
npm install dotenv-vault
bash
- 预执行钩子:
"pretypeorm": "node ./scripts/check-env.js"
json
2.4 调试配置
2.4.1 VSCode调试配置
{
"type": "node",
"request": "launch",
"name": "TypeORM CLI",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "typeorm"],
"console": "integratedTerminal"
}
json
2.4.2 常见错误处理
- 连接超时:
- 检查防火墙设置
- 验证网络策略
- 调整连接超时参数
- 时区问题:
timezone: 'Z' // UTC时区
typescript
2.5 企业级实践
2.5.1 配置中心集成
import { getConfig } from '@company/config-center';
const dbConfig = getConfig('database');
export default new DataSource({
...dbConfig,
entities: [/*...*/]
});
typescript
2.5.2 健康检查端点
import { DataSource } from 'typeorm';
export async function checkDBHealth() {
try {
const ds = new DataSource(/*config*/);
await ds.query('SELECT 1');
return true;
} catch {
return false;
}
}
typescript
💡生产环境建议:
- 禁用synchronize功能
- 使用迁移脚本管理结构变更
- 实现配置的版本控制
- 建立回滚机制
附录:TypeORM支持数据库列表
数据库 | 驱动名称 | 适用场景 |
---|---|---|
MySQL | mysql | 通用关系型 |
PostgreSQL | postgres | 复杂查询 |
SQLite | sqlite | 本地开发 |
MongoDB | mongodb | 文档存储 |
Oracle | oracle | 企业级应用 |
3. 数据库操作实践
3.1 执行Schema同步
3.1.1 同步机制详解
Schema同步是通过对比Entity定义与数据库现有结构的差异,自动生成并执行DDL语句的过程。TypeORM支持以下同步策略:
- 全量同步:完整重建数据库结构
- 增量同步:仅更新差异部分
- 安全模式:禁止删除已存在列
// 同步配置示例
{
synchronize: true, // 开发环境启用
migrationsRun: false, // 与迁移互斥
dropSchema: false // 是否删除重建
}
typescript
3.1.2 同步SQL生成规则
Entity定义 | 生成SQL | 风险等级 |
---|---|---|
新增@Column | ALTER TABLE ADD COLUMN | 低 |
修改@Column类型 | ALTER TABLE MODIFY COLUMN | 中 |
删除@Column | ALTER TABLE DROP COLUMN | 高 |
新增@Entity | CREATE TABLE | 低 |
3.1.3 生产环境注意事项
- 必须禁用synchronize
- 同步前备份数据
- 使用
--dry-run
参数预览SQL
npm run typeorm schema:sync -- --dry-run
bash
3.2 迁移操作最佳实践
3.2.1 迁移文件结构解析
生成的迁移文件包含两个核心方法:
export class InitialMigration implements MigrationInterface {
// 升级逻辑
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE user(...)`);
}
// 回滚逻辑
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE user`);
}
}
typescript
3.2.2 迁移工作流
3.2.3 高级迁移技巧
- 数据迁移:在up方法中插入初始化数据
await queryRunner.query(`INSERT INTO roles(name) VALUES ('admin')`);
typescript
- 条件执行:检查表是否存在
const tableExists = await queryRunner.hasTable('user');
typescript
- 事务控制:确保原子性
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.startTransaction();
try {
// 迁移操作
await queryRunner.commitTransaction();
} catch (err) {
await queryRunner.rollbackTransaction();
}
}
typescript
3.3 版本控制策略
3.3.1 迁移文件命名规范
[YYYYMMDDHHmmss]-[Description].ts
示例:202308151430-CreateUserTable.ts
text
3.3.2 团队协作流程
- 禁止直接修改已提交的迁移文件
- 通过新建迁移修复问题
- 使用
migrations:show
命令查看待执行迁移
npm run typeorm migration:show
bash
3.4 性能优化方案
3.4.1 批量操作优化
// 低效方式
for (const user of users) {
await repository.save(user);
}
// 高效方式
await repository.save(users);
typescript
3.4.2 索引管理
- 通过迁移添加索引
await queryRunner.createIndex('user', new TableIndex({
name: 'IDX_USER_EMAIL',
columnNames: ['email'],
isUnique: true
}));
typescript
- 在Entity定义索引
@Index('IDX_USER_NAME', ['firstName', 'lastName'])
@Entity()
export class User {}
typescript
3.5 常见问题解决方案
3.5.1 迁移失败处理
- 检查迁移表记录
SELECT * FROM migrations;
sql
- 手动修复后执行
npm run typeorm migration:run
bash
3.5.2 多数据库迁移
// 多数据源迁移配置
"scripts": {
"migrate:db1": "npm run typeorm migration:run -- -d ormconfig.db1.ts",
"migrate:db2": "npm run typeorm migration:run -- -d ormconfig.db2.ts"
}
typescript
附录:TypeORM CLI完整命令参考
命令 | 参数 | 说明 |
---|---|---|
schema:sync | --dry-run | 结构同步 |
migration:create | -n <name> | 创建空迁移 |
migration:generate | -n <name> | 根据差异生成 |
migration:run | -t <timestamp> | 执行迁移 |
migration:revert | -t <timestamp> | 回滚迁移 |
migration:show | - | 显示待执行迁移 |
entity:create | -n <name> | 创建Entity文件 |
💡最佳实践提示:
- 开发环境使用synchronize快速迭代
- 预发布环境必须使用迁移脚本
- 保持迁移脚本的幂等性
- 为每个迁移编写回滚逻辑
4. 多数据库连接管理
4.1 动态数据源切换
4.1.1 多数据源注册方案
// src/database/data-sources.ts
import { DataSource } from 'typeorm';
export const dataSources = {
db1: new DataSource({
name: 'db1',
type: 'mysql',
host: process.env.DB1_HOST,
// ...其他配置
}),
db2: new DataSource({
name: 'db2',
type: 'postgres',
host: process.env.DB2_HOST,
// ...其他配置
})
};
// 初始化所有数据源
export async function initializeDataSources() {
await Promise.all([
dataSources.db1.initialize(),
dataSources.db2.initialize()
]);
}
typescript
4.1.2 运行时动态切换
// 基于请求头切换数据源
function getDataSource(req: Request): DataSource {
return req.headers['x-db-version'] === 'v2'
? dataSources.db2
: dataSources.db1;
}
// 在服务层使用
const userRepository = getDataSource(request).getRepository(User);
typescript
4.1.3 事务隔离处理
async function transferBetweenDBs() {
const queryRunner1 = dataSources.db1.createQueryRunner();
const queryRunner2 = dataSources.db2.createQueryRunner();
try {
await queryRunner1.connect();
await queryRunner2.connect();
await queryRunner1.startTransaction();
await queryRunner2.startTransaction();
// 跨库业务逻辑
await queryRunner1.commitTransaction();
await queryRunner2.commitTransaction();
} catch (err) {
await queryRunner1.rollbackTransaction();
await queryRunner2.rollbackTransaction();
throw err;
} finally {
await queryRunner1.release();
await queryRunner2.release();
}
}
typescript
4.2 多数据库最佳实践
4.2.1 配置管理策略
// config/databases.ts
interface DatabaseConfig {
[key: string]: DataSourceOptions;
}
export const databases: DatabaseConfig = {
primary: {
name: 'primary',
type: 'mysql',
// ...公共配置
...require('./config/primary.json')
},
replica: {
name: 'replica',
type: 'mysql',
// ...公共配置
...require('./config/replica.json')
}
};
typescript
4.2.2 连接池监控
// 监控指标采集示例
setInterval(() => {
const db1Stats = dataSources.db1.driver.pool.getStatus();
const db2Stats = dataSources.db2.driver.pool.getStatus();
metrics.gauge('db1.connections.active', db1Stats.active);
metrics.gauge('db2.connections.idle', db2Stats.idle);
}, 5000);
typescript
4.2.3 故障转移机制
// 带重试的数据源获取
async function getDataSourceWithFallback() {
try {
if (!dataSources.db1.isInitialized) {
await dataSources.db1.initialize();
}
return dataSources.db1;
} catch (err) {
console.error('Primary DB failed, failing over to replica');
return dataSources.db2;
}
}
typescript
4.3 读写分离实现
4.3.1 基于注解的路由
// 自定义装饰器
export function ReadOnly() {
return SetMetadata('dataSource', 'replica');
}
// 拦截器实现
@Injectable()
export class DataSourceInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
const handler = context.getHandler();
const dataSource = Reflect.getMetadata('dataSource', handler) || 'primary';
const request = context.switchToHttp().getRequest();
request.dataSource = dataSources[dataSource];
return next.handle();
}
}
typescript
4.3.2 负载均衡策略
// 轮询选择读库
const readReplicas = [dataSources.replica1, dataSources.replica2];
let replicaIndex = 0;
function getReadDataSource() {
replicaIndex = (replicaIndex + 1) % readReplicas.length;
return readReplicas[replicaIndex];
}
typescript
4.4 性能优化方案
4.4.1 连接预热
// 服务启动时预热连接池
async function warmUpConnections() {
const warmupQueries = [
dataSources.db1.query('SELECT 1'),
dataSources.db2.query('SELECT 1')
];
await Promise.all(warmupQueries);
}
typescript
4.4.2 跨库查询优化
// 使用缓存减少跨库查询
async function getCrossDBData() {
const cacheKey = 'cross_db_data';
const cached = await cache.get(cacheKey);
if (cached) return cached;
const [db1Result, db2Result] = await Promise.all([
dataSources.db1.query('...'),
dataSources.db2.query('...')
]);
const combined = { db1Result, db2Result };
await cache.set(cacheKey, combined, 60);
return combined;
}
typescript
4.5 安全注意事项
4.5.1 权限隔离
-- 为每个数据源创建专用账号
CREATE USER 'app_primary'@'%' IDENTIFIED BY 'password1';
GRANT SELECT, INSERT, UPDATE ON primary_db.* TO 'app_primary'@'%';
CREATE USER 'app_replica'@'%' IDENTIFIED BY 'password2';
GRANT SELECT ON replica_db.* TO 'app_replica'@'%';
sql
4.5.2 审计日志
// 记录所有写操作
dataSources.db1.subscribe(event => {
if (event.type === 'query' && isWriteQuery(event.query)) {
auditLog.write({
query: event.query,
timestamp: new Date(),
user: currentUser
});
}
});
typescript
附录:多数据源架构模式
模式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
垂直分库 | 业务模块隔离 | 高隔离性 | 跨库join困难 |
水平分片 | 大数据量表 | 分散负载 | 需要分片键 |
读写分离 | 读多写少 | 提升读性能 | 复制延迟 |
多租户 | SaaS应用 | 资源隔离 | 管理复杂度高 |
💡生产环境建议:
- 使用服务网格管理数据库连接
- 实现自动故障检测和切换
- 定期测试故障恢复流程
- 监控跨数据库调用的性能指标
5. CLI高级用法
5.1 常用命令深度解析
5.1.1 Schema同步增强
# 带参数的同步命令
npm run typeorm schema:sync -- --config ormconfig.prod.ts
bash
可选参数:
--dry-run
:仅生成SQL不执行--config
:指定配置文件路径--dataSource
:指定数据源名称
5.1.2 迁移命令进阶
# 生成带时间戳的迁移文件
npm run typeorm migration:generate -- -n UpdateUser -t $(date +%Y%m%d%H%M%S)
# 执行特定迁移版本
npm run typeorm migration:run -- -t 20230815120000
bash
迁移生命周期钩子:
export class MigrationTemplate implements MigrationInterface {
async beforeUp(): Promise<void> {
console.log('即将执行升级');
}
async afterDown(): Promise<void> {
console.log('回滚已完成');
}
}
typescript
5.2 企业级脚本优化
5.2.1 安全增强配置
"scripts": {
"db:sync": "cross-env NODE_ENV=production npm run typeorm schema:sync -- --dry-run && npm run confirm && npm run typeorm schema:sync",
"db:migrate": "npm run typeorm migration:run -- --transaction all",
"db:rollback": "npm run typeorm migration:revert -- --transaction all",
"db:generate-migration": "npm run typeorm migration:generate -- --pretty",
"confirm": "read -p '确认执行SQL? (y/n)' choice && [ $choice == 'y' ]"
}
json
5.2.2 多环境支持
"scripts": {
"migrate:dev": "dotenv -e .env.dev npm run typeorm migration:run",
"migrate:prod": "dotenv -e .env.prod npm run typeorm migration:run -- --config ormconfig.prod.ts"
}
json
5.3 高级调试技巧
5.3.1 调试迁移脚本
# 在迁移文件中添加debugger语句
npm run typeorm migration:run -- --inspect-brk
bash
5.3.2 性能分析
# 记录迁移执行时间
time npm run typeorm migration:run
bash
5.4 自动化集成方案
5.4.1 CI/CD管道示例
# GitHub Actions配置
jobs:
db-migrate:
steps:
- run: npm install
- run: npm run db:migrate
env:
DATABASE_URL: ${{ secrets.PROD_DB_URL }}
yaml
5.4.2 预提交钩子
"husky": {
"hooks": {
"pre-commit": "npm run typeorm migration:generate -- --check"
}
}
json
5.5 扩展命令开发
5.5.1 自定义CLI命令
// src/cli/seed.ts
import { DataSource } from 'typeorm';
export class SeedCommand {
async run() {
const dataSource = new DataSource(/* config */);
await dataSource.initialize();
// 数据填充逻辑
}
}
typescript
5.5.2 集成到npm脚本
"scripts": {
"db:seed": "ts-node src/cli/seed.ts"
}
json
附录:TypeORM CLI完整参数表
参数 | 说明 | 适用命令 |
---|---|---|
-f | 指定配置文件 | 所有命令 |
-d | 指定数据源 | 所有命令 |
-t | 目标时间戳 | migration:* |
--transaction | 事务模式 | migration:* |
--pretty | 美化输出 | migration:generate |
--check | 检查未生成迁移 | migration:generate |
💡专家建议:
- 为每个迁移编写完整的up/down方法
- 使用
--dry-run
在生产环境前验证 - 将CLI命令纳入CI流程
- 定期清理旧迁移文件
↑